<?php
use Enums\Code;
use JetBrains\PhpStorm\NoReturn;
use Yaf\Application;
use Yaf\Config\Ini;
/**
 * 输出json
 * @param mixed   $data 返回的数据
 */
#[NoReturn]
function json(array $data): void
{
    header('Content-Type:application/json');
    echo json_encode($data);
    exit();
}

/**
 * 浏览器友好的变量输出
 * @param mixed         $var 变量
 * @param boolean $echo 是否输出 默认为true 如果为false 则返回输出字符串
 * @param string|null $label 标签 默认为空
 * @param integer $flags htmlspecialchars flags
 * @return void|string
 */
function dd(mixed $var, bool $echo = true, string $label = null, int $flags = ENT_SUBSTITUTE)
{
    $label = (null === $label) ? '' : rtrim($label) . ':';
    ob_start();
    var_dump($var);
    $output = ob_get_clean();
    $output = preg_replace('/\]\=\>\n(\s+)/m', '] => ', $output);
    if (PHP_SAPI=='cli') {
        $output = PHP_EOL . $label . $output . PHP_EOL;
    } else {
        if (!extension_loaded('xdebug')) {
            $output = htmlspecialchars($output, $flags);
        }
        $output = '<pre>' . $label . $output . '</pre>';
    }
    if ($echo) {
        echo($output);
    } else {
        return $output;
    }
}

/**
 * 获取输入参数 支持过滤和默认值
 * 使用方法:
 * <code>
 * I('id',0); 获取id参数 自动判断get或者post
 * I('post.name','','htmlspecialchars'); 获取$_POST['name']
 * I('get.'); 获取$_GET
 * </code>
 *
 * @param string $name 变量的名称 支持指定类型
 * @param string|array $default 不存在的时候默认值
 * @param string|null $filter 参数过滤方法
 * @param string|array|null $datas 要获取的额外数据源
 * @return string|array|null
 */
function I(string $name, string|array $default = '', string|null $filter = null, string|array|null $datas = null):string|array|null {
    if (strpos ( $name, '/' )) { // 指定修饰符
        list ( $name, $type ) = explode ( '/', $name, 2 );
    }
    if (strpos ( $name, '.' )) { // 指定参数来源
        list ( $method, $name ) = explode ( '.', $name, 2 );
    } else { // 默认为自动判断
        $method = 'param';
    }
    switch (strtolower ( $method )) {
        case 'get' :
            $input = & $_GET;
            break;
        case 'post' :
            $input = & $_POST;
            break;
        case 'put' :
            parse_str ( file_get_contents ( 'php://input' ), $input );
            break;
        case 'param' :
            switch ($_SERVER ['REQUEST_METHOD']) {
                case 'POST' :
                    $input = $_POST;
                    break;
                case 'PUT' :
                    parse_str ( file_get_contents ( 'php://input' ), $input );
                    break;
                default :
                    $input = $_GET;
            }
            break;
        case 'request' :
            $input = & $_REQUEST;
            break;
        case 'session' :
            $input = & $_SESSION;
            break;
        case 'cookie' :
            $input = & $_COOKIE;
            break;
        case 'server' :
            $input = & $_SERVER;
            break;
        case 'data' :
            $input = & $datas;
            break;
        default :
            return NULL;
    }
    if ('' == $name) { // 获取全部变量
        $data = $input;
        $filters = $filter ?? 'htmlspecialchars';
        if ($filters) {
            $filters = explode ( ',', $filters );
            foreach ( $filters as $filter ) {
                $data = array_map_recursive ( $filter, $data ); // 参数过滤
            }
        }
    } elseif (isset ( $input [$name] )) { // 取值操作
        $data = $input [$name];
        $filters = $filter ?? 'htmlspecialchars';
        if ($filters) {
            $filters = explode ( ',', $filters );
            foreach ( $filters as $filter ) {
                if (function_exists ( $filter )) {
                    $data = is_array ( $data ) ? array_map_recursive ( $filter, $data ) : $filter ( $data ); // 参数过滤
                } elseif (str_starts_with($filter, '/')) {
                    // 支持正则验证
                    if (1 !== preg_match ( $filter, ( string ) $data )) {
                        return $default ?? NULL;
                    }
                } else {
                    $data = filter_var ( $data, is_int ( $filter ) ? $filter : filter_id ( $filter ) );
                    if (false === $data) {
                        return $default ?? NULL;
                    }
                }
            }
        }
        if (! empty ( $type )) {
            $data = match (strtolower($type)) {
                's' => ( string )$data,
                'a' => ( array )$data,
                'd' => ( int )$data,
                'f' => ( float )$data,
                'b' => ( boolean )$data,
            };
        }
    } else { // 变量默认值
        $data = $default ?? NULL;
    }
    is_array ( $data ) && array_walk_recursive ( $data, 'yaf_filter' );
    return $data;
}

/**
 * sql安全过滤
 * @param $value
 */
function yaf_filter(&$value): void
{
    // TODO 其他安全过滤
    // 过滤查询特殊字符
    if (preg_match ( '/^(EXP|NEQ|GT|EGT|LT|ELT|OR|XOR|LIKE|NOTLIKE|NOT BETWEEN|NOTBETWEEN|BETWEEN|NOTIN|NOT IN|IN)$/i', $value )) {
        $value .= ' ';
    }
}

/**
 * @param $filter
 * @param $data
 * @return array
 */
function array_map_recursive($filter, $data): array
{
    $result = array ();
    foreach ( $data as $key => $val ) {
        $result [$key] = is_array ( $val ) ? array_map_recursive ( $filter, $val ) : call_user_func ( $filter, $val );
    }
    return $result;
}

/**
 *  获取客户端IP地址
 * @return string
 */
function get_client_ip():string {
    if (isset($_SERVER)) {
        $ip = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['HTTP_CLIENT_IP'] ?? $_SERVER['REMOTE_ADDR'];
    } else {
        if (getenv('HTTP_X_FORWARDED_FOR')) {
            $ip = getenv('HTTP_X_FORWARDED_FOR');
        } else if (getenv('HTTP_CLIENT_IP')) {
            $ip = getenv('HTTP_CLIENT_IP');
        } else {
            $ip = getenv('REMOTE_ADDR');
        }
    }
    return $ip;
}

/**
 * XML编码
 *
 * @param array $data 数据
 * @param string $root 根节点名
 * @param string $item 数字索引的子节点名
 * @param string $attr 根节点属性
 * @param string $id   数字索引子节点key转换的属性名
 * @param string $encoding	数据编码
 * @return string
 */
function xml_encode(array $data, string $root = 'yaf', string $item = 'item', string $attr = '', string $id = 'id', string $encoding = 'utf-8'): string
{
    if (is_array ( $attr )) {
        $_attr = array ();
        foreach ( $attr as $key => $value ) {
            $_attr [] = "{$key}=\"{$value}\"";
        }
        $attr = implode ( ' ', $_attr );
    }
    $attr = trim ( $attr );
    $attr = empty ( $attr ) ? '' : " {$attr}";
    $xml = "<?xml version=\"1.0\" encoding=\"{$encoding}\"?>";
    $xml .= "<{$root}{$attr}>";
    $xml .= data_to_xml ( $data, $item, $id );
    $xml .= "</{$root}>";
    return $xml;
}

/**
 * 数据XML编码
 * @param array $data	数据
 * @param string $item	数字索引时的节点名称
 * @param string $id 数字索引key转换为的属性名
 * @return string
 */
function data_to_xml(array $data, string $item = 'item', string $id = 'id'): string
{
    $xml = $attr = '';
    foreach ( $data as $key => $val ) {
        if (is_numeric ( $key )) {
            $id && $attr = " {$id}=\"{$key}\"";
            $key = $item;
        }
        $xml .= "<{$key}{$attr}>";
        $xml .= (is_array ( $val ) || is_object ( $val )) ? data_to_xml ( $val, $item, $id ) : $val;
        $xml .= "</{$key}>";
    }
    return $xml;
}

/**
 * 获取随机字符串
 * @param int $length 获取随机字符串的长度,默认为6
 * @param string $strPol 备选字符组成的字符串,默认为A-Za-z0-9;
 * @return string        获取的随机字符串
 */
function get_rand_char(int $length = 6, string $strPol = ''):string
{
    $str = '';
    $strPol = $strPol == '' ? "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz" : $strPol;
    $max = strlen($strPol) - 1;
    for ($i = 0; $i < $length; $i++) {
        $str .= $strPol[rand(0, $max)];//rand($min,$max)生成介于min和max两个数之间的一个随机整数
    }
    return $str;
}

/**
 * 获取随机数
 * @param int $length 长度
 * @return string
 */
function get_rand_int(int $length = 6): string
{
    $str = '';
    $array = range(0, 9);
    for($i = 0; $i < $length; $i++) {
        shuffle($array);
        $str .= $array[rand(0, 9)];
    }
    return $str;
}

/**
 * 数组格式化
 * @param array $result 数据库查询结果集
 * @param string $by_filed 格式化后数组的key,默认为ID
 * @return array 返回格式化后数组
 */
function array_format(array $result = array(), string $by_filed = 'id'): array
{
    $format_array = array();
    foreach ($result as $row) {
        $format_array[$row[$by_filed]] = $row;
    }
    return $format_array;
}

/**
 * 生成密码
 * @param string $password
 * @param string $salt
 * @return string
 */
function generate_password(string $password, string $salt): string
{
    return password_hash($password.$salt,PASSWORD_BCRYPT);
}

/**
 * 加密
 * @param string $str 要加密的数据
 * @return bool|string   加密后的数据
 */
function yaf_encrypt(string $str): bool|string
{
    $secretKey = Application::app()->getConfig()->application->passphrase;
    $data = openssl_encrypt($str, 'AES-128-ECB', $secretKey, OPENSSL_RAW_DATA);
    return str_replace("+",".",base64_encode($data));
}

/**
 * 解密
 * @param string $str 要解密的数据
 * @return string        解密后的数据
 */
function yaf_decrypt(string $str):string
{
    $secretKey = Application::app()->getConfig()->application->passphrase;
    $str = str_replace(".","+",$str);
    return openssl_decrypt(base64_decode($str), 'AES-128-ECB', $secretKey, OPENSSL_RAW_DATA);
}

/**
 * 获取当前请求域名
 * @return string
 */
function getRequestHost():string
{
    $basename = basename(__ROOT__);
    $http_type = ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')) ? 'https://' : 'http://';
    if ($basename) {
        return $http_type . $_SERVER['HTTP_HOST'] . '/' . $basename . "/";
    }else {
        return $http_type . $_SERVER['HTTP_HOST'];
    }
}

/**
 * 快速文件数据读取和保存 针对简单类型数据 字符串、数组
 *
 * @param string $name 缓存名称
 * @param mixed $value 缓存值
 * @param string $path 缓存路径
 * @return mixed
 * @throws Exception
 */
function fileStorage(string $name, mixed $value = '', string $path = RUNTIME_PATH.'/data/'): mixed
{
    static $_cache = array ();
    $filename = $path . $name . '.php';
    if ('' !== $value) {
        if (is_null ( $value )) {
            // 删除缓存
            if (str_contains($name, '*')) {
                return false; // TODO
            } else {
                unset ( $_cache [$name] );
                return FileStorage::unlink ( $filename, 'F' );
            }
        } else {
            FileStorage::put ( $filename, serialize ( $value ), 'F' );
            // 缓存数据
            $_cache [$name] = $value;
            return null;
        }
    }
    // 获取缓存数据
    if (isset ( $_cache [$name] ))
        return $_cache [$name];
    if (FileStorage::has ( $filename, 'F' )) {
        $value = unserialize ( FileStorage::read ( $filename, 'F' ) );
        $_cache [$name] = $value;
    } else {
        $value = false;
    }
    return $value;
}

/**
 * 获取所有未删除数据
 * @param string $table 表名
 * @param array $sort 排序字段
 * @return array|string $result 结果数组
 * @throws Exception
 */
function get_no_del(string $table = '', array $sort = ['sort'=>'desc'],string $keyBy = "id"):array|string
{
    if (!$table) return array();
    /**以表名_all命名缓存*/
    $file = $table . '_no_del';
    try {
        $config = new Ini(__ROOT__ . "/conf/db.ini");
        $db = $config->get("db");
        if (!fileStorage($file)) {
            $key = array_keys($sort);
            $all = DB::table($db->prefix.$table)->where("is_hid", 0)->whereNull("deleted_at")
                ->orderBy($key[0], $sort[$key[0]])->get()->keyBy($keyBy)->toArray();
            if ($all) {
                foreach ($all as $key=>$vo) {
                    $all[$key] = (array)$vo;
                }
            }
            fileStorage($file, $all);
        }
    } catch (Exception $e) {
        e($e->getMessage());
    }
    return fileStorage($file);
}

/**
 * 获取所有未禁用数据
 * @param string $table 表名
 * @param array $sort 排序字段
 * @return array|string $result 结果数组
 * @throws Exception
 */
function get_no_hid(string $table = '', array $sort = ['sort'=>'desc'],string $keyBy = "id"):array|string
{
    if (!$table) return array();
    /**以表名_all命名缓存*/
    $file = $table . '_no_hid';
    try {
        if (!fileStorage($file)) {
            $key = array_keys($sort);
            $all = DB::table($table)->where("is_hid", 0)->where("is_hid",0)
                ->orderBy($key[0], $sort[$key[0]])->get()->keyBy($keyBy)->toArray();
            if ($all) {
                foreach ($all as $key=>$vo) {
                    $all[$key] = (array)$vo;
                }
            }
            fileStorage($file, $all);
        }

    } catch (Exception $e) {
        echo $e->getMessage();
    }
    return fileStorage($file);
}

/**
 * Ajax方式返回数据到客户端
 *
 * @param array $data 要返回的数据
 * @param String $type AJAX返回数据格式
 * @param int $json_option 传递给json_encode的option参数
 * @return void
 */
#[NoReturn] function ajaxReturn(array $data, string $type = 'JSON', int $json_option = 0): void
{
    switch (strtoupper($type)) {
        case 'JSON' :
            if (extension_loaded("xhprof")) {
                if (IS_XHPROF) {
                    $xhprof_data = xhprof_disable();
                    $xhprof_runs = new XHProfRuns_Default();
                    $xhprof_runs->save_run($xhprof_data, "xhprof_foo");
                }
            }
            // 返回JSON数据格式到客户端 包含状态信息
            header('Content-Type:application/json; charset=utf-8');
            exit (json_encode($data, $json_option));
        case 'XML' :
            // 返回xml格式数据
            header('Content-Type:text/xml; charset=utf-8');
            exit (xml_encode($data));
        case 'JSONP' :
            // 返回JSON数据格式到客户端 包含状态信息
            header('Content-Type:application/json; charset=utf-8');
            exit ('jsonpReturn' . '(' . json_encode($data, $json_option) . ');');
        case 'EVAL' :
            // 返回可执行的js脚本
            header('Content-Type:text/html; charset=utf-8');
            exit ($data);
        default :
            exit("类型错误");
    }
}

/**
 * 接口错误输出
 * @param string $msg 提示信息
 * @param int|null $status 状态
 * @param array $data 需要返回的数据
 * @return void
 */
#[NoReturn] function apiError(string $msg, ?int $status = null, array $data = []): void
{
    $return = [
        'code' => $status ?: Code::ERROR->value,
        'message' => $msg,
        'data' => $data
    ];
    ajaxReturn($return);
}

/**
 * 接口成功输出
 * @param array $data 需要返回的数据
 * @param string $msg 提示信息
 * @param int|null $status 状态
 * @return void
 */
#[NoReturn] function apiSuccess(array $data = [], string $msg = 'success', ?int $status = null): void
{
    $return = [
        'code' => $status ?: Code::SUCCESS->value,
        'message' => $msg,
        'data' => $data
    ];
    ajaxReturn($return);
}

/**
 * 获取客户端系统类型
 * @return string
 */
function getOS(): string
{
    $userAgent = $_SERVER['HTTP_USER_AGENT'];
    // 对User-Agent进行解析以获取操作系统信息
    $osPlatform  = "";
    // 检查常见操作系统的关键字
    if (stripos($userAgent, 'Windows NT 10.0') !== false) {
        $osPlatform = 'Windows 10';
    } elseif (stripos($userAgent, 'Windows NT 6.3') !== false) {
        $osPlatform = 'Windows 8.1';
    } elseif (stripos($userAgent, 'Windows NT 6.2') !== false) {
        $osPlatform = 'Windows 8';
    } elseif (stripos($userAgent, 'Windows NT 6.1') !== false) {
        $osPlatform = 'Windows 7';
    } elseif (stripos($userAgent, 'Windows NT 6.0') !== false) {
        $osPlatform = 'Windows Vista';
    } elseif (stripos($userAgent, 'Windows NT 5.1') !== false) {
        $osPlatform = 'Windows XP';
    } elseif (stripos($userAgent, 'Windows NT 5.0') !== false) {
        $osPlatform = 'Windows 2000';
    } elseif (stripos($userAgent, 'iPhone') !== false) {
        $osPlatform = 'iOS';
    } elseif (stripos($userAgent, 'Macintosh') !== false) {
        $osPlatform = 'Mac OS';
    } elseif (stripos($userAgent, 'Linux') !== false) {
        $osPlatform = 'Linux';
    } elseif (stripos($userAgent, 'Android') !== false) {
        $osPlatform = 'Android';
    } else {
        $osPlatform = '未知';
    }
    return $osPlatform;
}

/**
 * 获取客户端浏览器类型
 * @return string
 */
function getBrowser(): string
{
    $userAgent = $_SERVER['HTTP_USER_AGENT'];
    // 判断浏览器类型
    if (stripos($userAgent, 'Firefox') !== false) {
        return 'Mozilla Firefox';
    } elseif (preg_match('/Edge\/\d+./i', $userAgent)) {
        return 'Microsoft Edge';
    } elseif (stripos($userAgent, 'Chrome') !== false) {
        return 'Google Chrome';
    } elseif (stripos($userAgent, 'Safari')) {
        return 'Apple Safari';
    } elseif (preg_match('/Trident\/\d+/i', $userAgent) && stripos($userAgent, 'rv:') !== false) {
        return 'Internet Explorer';
    } elseif (stripos($userAgent, 'Opera Mini') !== false) {
        return 'Opera Mini';
    } elseif (stripos($userAgent, 'Opera') || stripos($userAgent, 'OPR/') !== false) {
        return 'Opera';
    } else {
        return '未知';
    }
}

/**
 * 获取客户端IP地址归属
 * @return array|mixed
 */
function getIpAddress(): mixed
{
    $ip = get_client_ip();
    if ($ip == '127.0.0.1') {
        $ipData['address'] = "本地主机";
    }else {
        $url = "https://qifu-api.baidubce.com/ip/geo/v1/district?ip=" . $ip;
        $result = json_decode(file_get_contents($url), true);
        if ($result) {
            if ($result['status'] == "Success") {
                $ipData = $result['data'];
                $ipData['address'] = $ipData['prov'] . $ipData['city'] .$ipData['district'] ." ". $ipData['isp'];
            } else {
                $ipData['address'] = "未知";
            }
        } else {
            $ipData['address'] = "未知";
        }
    }
    return $ipData;
}

/**
 * 获取数据字典数据
 * @param string $key
 * @return array|mixed
 */
function dict(string $key): mixed
{
    $keys = explode(".",$key);
    if (count($keys)>1) {
        $data = DictModel::query()
            ->where("group_name",$keys[0])
            ->where("name",$keys[1])
            ->where("is_hid",0)
            ->first()->toArray();
        return $data['value']??NULL;
    }else {
        $data = DictModel::query()
            ->where("group_name",$keys[0])
            ->where("is_hid",0)
            ->selectRaw("name,value")
            ->get()->toArray();
        $dict = [];
        foreach ($data as $vo) {
            $dict[$vo['name']] = $vo['value'];
        }
        return $dict;
    }
}

function dictByName(string $key): mixed
{
    $keys = explode(".",$key);
    if (count($keys)>1) {
        $data = DictModel::query()
            ->where("group_name",$keys[0])
            ->where("name",$keys[1])
            ->where("is_hid",0)
            ->first()->toArray();
        return $data['value']??NULL;
    }else {
        $data = DictModel::query()
            ->where("group_name",$keys[0])
            ->where("is_hid",0)
            ->selectRaw("name,value")
            ->get()->toArray();
        $dict = [];
        foreach ($data as $vo) {
            $dict[$vo['value']] = $vo['name'];
        }
        return $dict;
    }
}

/**
 * 根据姓名生成文字头像
 * @param string $member_id
 * @param string $txt
 * @return string
 */
 function makeTxtAvatar(string $member_id,string $txt): string
{
    // 建立一幅 $imageW x $imageH 的图像
    $im = imagecreate(100, 100);
    $colorLst = [
        [24,144,255],
        [244,105,0],
        [0,162,173],
        [114,102,230],
    ];
    $mt_color = $colorLst[array_rand($colorLst)];
    // 设置背景
    imagecolorallocate( $im, $mt_color[0], $mt_color[1], $mt_color[2]);
    $color = imagecolorallocate( $im, 255, 255, 255);
    $fontttf = APP_PATH. '/library/fonts/2.TTF';
    $characters = preg_split('/(?<!^)(?!$)/u', $txt);
    if (count($characters)>2) {
        $text = [$characters[count($characters)-2],$characters[count($characters)-1]];
    }else {
        $text = $characters;
    }
    foreach ($text as $index => $char) {
        $x = 20 * ($index + 1)*1.5-6;
        $y = 20 * 2 *  1.5;
        imagettftext( $im, 20, 0, (int)$x, $y, $color, $fontttf, $char);
    }
    $dir = PUBLIC_PATH . '/uploads/avatar/';
    if (! is_dir ( $dir )) {
        mkdir($dir, 0755, true);
    }
    $file_path = $dir.md5($member_id).".png";
    // 输出图像
    imagepng( $im,$file_path);
    imagedestroy( $im);
    $path = './public/uploads/avatar/'.md5($member_id).".png";
    return getRequestHost().$path;
}

/**
 * 快速获取容器中的实例 支持依赖注入
 * @param string $name 类名或标识
 * @param array $args 参数
 * @return mixed|object|null
 * @throws Exception
 */
function app(string $name, array $args = []): mixed
{
    return App::make($name, $args);
}

